package impl.agentt.enseignant;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.logging.Logger;

import reservation.PlageHoraire;

import apps.BehTB;

import fr.irit.smac.may.lib.components.messaging.receiver.AgentRef;
import fr.irit.smac.may.lib.interfaces.Push;
import fr.irit.smac.may.lib.pmbehaviour.PatternMatchingBehavior;
import contraintes.ContrainteDispo;
import contraintes.Seance;
import debug.message.EnsDebugText;
import message.EDTMessage;
import message.PresencePlacee;
import message.presence.AcceptePlacement;
import message.presence.EnvoiePresence;
import message.presence.InitPresenceEnsAgent;
import message.presence.RefusPlacement;
import mysticedt.Behaviour;

public class BehaviourEnsImpl extends Behaviour {

	/**
	 * Liste des disponibilite de l'enseignant.
	 */
	private ArrayList<ContrainteDispo> listContrainteDispo;
	/**
	 * Liste des seances que l'enseignant doit dispenser.
	 */
	private ArrayList<Seance> listSeance;
	/**
	 * Table des agents pEns, avec leur criticite.
	 */
	private Hashtable<AgentRef, Integer> listP;
	/**
	 * Table des id des seances, avec les agent pEns concernes.
	 */
	private Hashtable<Integer,ArrayList<AgentRef>> linkIdSP;
	/**
	 * Criticite de l'agent enseignant.
	 */
	private int criticite = 0;

	PatternMatchingBehavior pmb = new PatternMatchingBehavior(this);

	public void defaultCase(Object message) {
		Logger log = Logger.getLogger("alerte");
		log.info("Alerte par "+this.getClass().getCanonicalName()+" : Incomprehension du message "+message.getClass().getCanonicalName());
	}
	
	/**
	 * Reception d'une demande d'initialisation
	 * - recuperation de sa liste de disponibilite
	 * - recuperation de sa liste de seance
	 * - creation de ses agents pEns
	 */
	public void caseInitEnseignantAgent() {
		this.listContrainteDispo = constraintAccess().getContraintesDispo();
		
		this.listSeance = subscriptionAccess().getListIdCoursEns();
		
		this.listP = new Hashtable<AgentRef, Integer>();
		this.linkIdSP = new Hashtable<Integer,ArrayList<AgentRef>>();
		for (Iterator<Seance> it = listSeance.iterator(); it.hasNext();) {
			Seance current = it.next();
			AgentRef pEnsRef = createPresenceAgent().createPresenceEnseignantAgent();
			debugText("Creation %s : OK",pEnsRef.getName());
			listP.put(pEnsRef,0);
			if(linkIdSP.get(current.getId())==null) {
				linkIdSP.put(current.getId(), new ArrayList<AgentRef>());
				linkIdSP.get(current.getId()).add(pEnsRef);
			} else
				linkIdSP.get(current.getId()).add(pEnsRef);
			send().send(new InitPresenceEnsAgent(current, myRef().pull(), listContrainteDispo), pEnsRef);
			criticite++;
		}
		debugText("Initialisation : OK");
	}

	/**
	 * Methode appelee lorsqu'un agent recoit un message de type PresencePlacee.
	 * 
	 * @param a : l'agent presence place
	 */
	public void casePresencePlacee(AgentRef a, PlageHoraire pH) {
		if(BehTB.plageHoraireIsInContrainteDispoListe(pH, listContrainteDispo)) {
			debugText("Placement de %s : OK",a.getName());
			BehTB.retirerDispo(pH,listContrainteDispo);
			send().send(new AcceptePlacement(), a);
			listP.put(a,0);
			criticite--;
			if(criticite==0){
				debugText("Placement de tous mes cours : OK");
			}
		} else {
			debugText("Placement : Impossible");
			debugText("Envoie refus a %s",a.getName());
			send().send(new RefusPlacement(listContrainteDispo), a);
		}
	}
	
	/*
	 * TODO Une pEtu demande toutes les presence/seance d'un cours pour pouvoir
	 * s'y inscrire caseFindSeanceCours(AgentRef exp,...)
	 */
	public void caseDemandePresences(AgentRef a, Integer ids) {
		debugText("Reception demande pour la seance %s par %s",ids,a.getName());
		debugText(linkIdSP.get(ids).toString());
		send().send(new EnvoiePresence(linkIdSP.get(ids)), a);
	}
	
	/**
	 * Methode appelee lorsqu'un agent recoit un message de type
	 * PresenceDeplacee.
	 * 
	 * @param a
	 */
	// Une presence ancienne placee notifie qu'elle n'est plus placee
	// exemple : la pEns a cedee sa reservation a une autre pEns plus critique
	public void casePresenceDeplacee(AgentRef a, Integer i) {
		listP.put(a, i);
		criticite++;
	}

	/**
	 * Methode appelee lorsqu'un agent recoit un message de type PresenceCrit.
	 * 
	 * @param a
	 */
	// une presence notifie un changement de sa criticite a son enseigant pour
	// le mettre a jour
	public void casePresenceCrit(AgentRef a, Integer i) {
		listP.put(a, i);
	}
	
	public void casePlacementImpossible(AgentRef exp, ArrayList<ContrainteDispo> listCDisp) {
		if(listContrainteDispo.equals(listCDisp))
			debugText("Reception notification placementImpossible par %s",exp.getName());
	}
	
	/*
	 * TODO Une pEns demande toutes les presences de l'enseignant deja placees
	 * apres son precedent SNC 5 a voir ce qu'on passe en parametre (la seance,
	 * l'horaire, la ref du precedent?) public ArrayList<AgentRef>
	 * caseAfterP(AgentRef exp, ...)
	 */

	/*
	 * TODO Demande d'une pEns de creer un nouveau cour SNC 3 voir parametre
	 * utiles caseDemandeCours(AgentRef exp, ...)
	 */

	@Override
	protected Push<EDTMessage> cycle() {
		return new Push<EDTMessage>() {
			public void push(EDTMessage message) {
				pmb.match(message);
			}

		};
	}
	
	
	protected void debugText(String s , Object... params){
		if(debug().isDebugON()){
			debug().sendDebugMessage(new EnsDebugText(myRef().pull(),String.format(s, params)));
		}
	}
	

}
